
/*
    Simple and easier game eng.
*/

#pragma once
#include "seg.h"
#ifndef _SEG_LIBC
#define _SEG_LIBC

auto seg::Times::get_ticks_ns(){
    return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
};

long long seg::Times::get_ticks()
{
    return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}

unsigned int seg::Times::Clock::get_fps()
{
    return this->fps;
}
void seg::Times::Clock::tick(int t)
{
    this ->Tick = t + 8;
    //Sleep((DWORD)(1000.0/t));
}
void seg::Times::Clock::tick()
{
    if (this->Tick < 900) Sleep((1000 / this->Tick));
    (this->frame)++;
    long long fmt_tmp = seg::Times::get_ticks_ns();
    unsigned int fmin_tmp = (unsigned int)(1000000.0 / ((fmt_tmp - this->fps_min_tick)/*>0 ? (fmt_tmp - this->fps_min_tick) : 0.1*/) );
    if (fmin_tmp != 0 && fmin_tmp < this->fps_min) this->fps_min = fmin_tmp;
    this->fps_min_tick = fmt_tmp;
    if (time(nullptr) > this->T0){
        this->T0 = time(nullptr);
        this->fps = this->frame;
        this->frame = 0;
        this->tps = this->ticks_in_one_second;
        this->ticks_in_one_second = 0;
        this->fps_min_ask = this->fps_min;
        this->fps_min = 65535;
    }
}
void seg::Times::Clock::init()
{
    this->T0 = time(nullptr);
    this->Tick = 60;
    this->frame = 0;
    this->fps = 0;
    this->ticks_in_one_second = 0;
    this->tps = 0;
    //
    this->fps_min_tick = seg::Times::get_ticks_ns();
    this->fps_min = UINT_MAX;
    this->fps_min_ask = 0;
}


void seg::render::text(int x, int y, TCHAR *s, seg::render::Place dtype)
{
    switch (dtype)
    {
    case seg::render::lefttop:{
        outtextxy(int(x), (int)y, static_cast<LPCTSTR>(s));
    }
        break;
    case seg::render::righttop: {
        outtextxy(int((int)x - textwidth(s)), (int)y, (LPCTSTR)s);
    }
        break;
    case seg::render::leftbottom: {
        outtextxy(int(x), (int)y - textheight(s), (LPCTSTR)s);
    }
        break;
    case seg::render::rightbottom: {
        outtextxy(int(x) - textwidth(s), (int)y - textheight(s), (LPCTSTR)s);
    }
        break;
    //
    case seg::render::left:{
        outtextxy(int(x), (int)y - int(textheight(s) / 2), static_cast<LPCTSTR>(s));
    }
        break;
    case seg::render::top:{
        outtextxy(int(x) - int(textwidth(s) / 2), (int)y, static_cast<LPCTSTR>(s));
    }
        break;
    case seg::render::right:{
        outtextxy(int(x) - textwidth(s), (int)y - int(textheight(s) / 2), static_cast<LPCTSTR>(s));
    }
        break;
    case seg::render::bottom:{
        outtextxy(int(x) - int(textwidth(s) / 2), (int)y - textheight(s), static_cast<LPCTSTR>(s));
    }
        break;
    case seg::render::center:{
        outtextxy(int(x) - int(textwidth(s) / 2), (int)y - int(textheight(s) / 2), static_cast<LPCTSTR>(s));
    }
        break;

    default:
        outtextxy(int(x), (int)y, static_cast<LPCTSTR>(s));
        break;
    }
}


void seg::EasyX_Initialize(int width, int height, LPCWSTR WinTitle)
{
    seg::MainHandle = initgraph(width, height, 0);
    seg::Window.width = width;
    seg::Window.height = height;
    seg::Window.SCR = GetWorkingImage();
    SetWindowTextW(seg::MainHandle, WinTitle);
    setbkmode(TRANSPARENT);
    BeginBatchDraw();
}
void seg::EasyX_Initialize(int w, int h, LPCWSTR WinTitle, int flag)
{
    seg::MainHandle = initgraph(w, h, flag);
    seg::Window.width = w;
    seg::Window.height = h;
    seg::Window.SCR = GetWorkingImage();
    SetWindowTextW(seg::MainHandle, WinTitle);
    setbkmode(TRANSPARENT);
    BeginBatchDraw();
}

int seg::ClickableObject::_on_click()
{
    int ret = 0;
    if (this->ClickAsInput == false)
    {
        seg::ClickableObject* temp = this;
        ret = this->OnClick(*temp);
    }
    else
    {
        pass;//拦截该消息
    }
    return ret;
}

int seg::ClickableObject::_on_click(MOUSEMSG & _m, double perx, double pery)
{
    int ret = 0;
    if (this->ClickAsInput == false)
    {
        seg::ClickableObject* temp = this;
        ret = this->OnClickEx(*temp, _m, perx, pery);
    }
    else
    {
        pass;//拦截该消息
    }
    return ret;
}

void seg::ClickableObject::_render()
{
    if (this->AutoRender == true)
    {
        setlinecolor(this->fg);
        settextcolor(this->TextColor);
        setfillcolor(this->bg);
        setlinestyle(this->LineStyle, this->LineWidth);
        fillrectangle(this->x, this->y, (this->x + this->width), (this->y + this->height));
        rectangle(this->x, this->y, (this->x + this->width), (this->y + this->height));
        if (this->s.count_line() > 1)
        {
            seg::render::multiline
            (
                this->x, this->y, this->s, (this->height / this->s.count_line()), seg::render::Place::lefttop
            );
        } else
        seg::render::text(
            (this->x + this->x + this->width) / 2,
            (this->y + this->y + this->height ) / 2,
            (TCHAR*)(this->s.get()),
            seg::render::Place::center
        );
    }
    else
    {
        seg::ClickableObject* temp = this;
        this->render(*temp);
    }
}

void seg::NoneTypeAllFuncPtr(seg::ClickableObject & p)
{
    return;
}

int seg::NoneTypeOfFuncPtr(seg::ClickableObject & p)
{
    return 0;
}

int seg::NoneTypeOfFuncPtrEx(seg::ClickableObject & p, MOUSEMSG & m, double perx, double pery)
{
    return 0;
}

void seg::ClickableObject::init(
    int x0, int y0, int w0, int h0, int lw, int ct,
    COLORREF fg0, COLORREF bg0, bool ar, int Id, bool cai, bool uoce, COLORREF tc, int ls
)
{
    this->x = x0;
    this->y = y0;
    this->width = w0;
    this->height = h0;
    this->LineWidth = lw;
    this->ComType = ct;
    this->fg = fg0;
    this->bg = bg0;
    this->AutoRender = ar;
    this->id = Id;
    this->ClickAsInput = cai;
    this->s.set(L"\0\0");
    this->UsingOnclickEx = uoce;
    this->TextColor = tc;
    this->LineStyle = ls;
    this->OnClick = seg::NoneTypeOfFuncPtr;
    this->render  = seg::NoneTypeAllFuncPtr;
    this->OnClickEx = seg::NoneTypeOfFuncPtrEx;
}

seg::Interface::Interface(int num, COLORREF BG, bool AutoExitBtn)
{
    this->num = num;
    this->ChooseID = -114514;
    this->bg = BG;
    this->CreateAutoQuitButton = AutoExitBtn;
    this->ShowDebugInfo = false;
    this->dbgifo.set(L"");
    this->dbgifo_livetime = 0;
    this->cbs = new seg::ClickableObject[num]; // 记得释放
}

bool seg::Interface::CheckChoose(MOUSEMSG Mmsg)
{
    int xpos = Mmsg.x;
    int ypos = Mmsg.y;
    int x, y;
    int xr, yr;
    int ret = 0;
    if (this->CreateAutoQuitButton == true)
    {
        if (0 <= xpos && xpos <= 50 && 0 <= ypos && ypos <= 50)
        {
            return true;
        }
    }
    for (int i = this->num - 1; i >= 0; i--)
    {
        x = this->cbs[i].x;
        y = this->cbs[i].y;
        xr= x + this->cbs[i].width;
        yr= y + this->cbs[i].height;
        if (x <= xpos && xpos <= xr && y <= ypos && ypos <= yr)
        {
            // click
            if (this->ShowDebugInfo == true)
            {this->dbgifo.set(L"OC ");
            this->dbgifo.join((int)(xpos));
            this->dbgifo.join(wchar_t(','));
            this->dbgifo.join(ypos);
            this->dbgifo.join(wchar_t(':'));
            this->dbgifo.join(i);
            this->dbgifo_livetime = 125 * 2.080;}
            if (this->cbs[i].UsingOnclickEx == false)
            {
                ret = this->cbs[i]._on_click();
            }
            else
            {
                double px = (double)(xpos - x) / this->cbs[i].width;
                double py = (double)(ypos - y) / this->cbs[i].height;
                ret = this->cbs[i]._on_click(Mmsg, px, py );
            }
            if (this->cbs[i].ClickAsInput == true)
            {
                this->ChooseID = i;
            }
            if (ret == 0)
            {
                return false;
            }
            else {
                return true; //
            }
        }
    }
    // None
    this->ChooseID = -1;
    return false;
}

void seg::Interface::mainloop()
{
    //LINESTYLE OldLS;
    //getlinestyle(&OldLS);
    MOUSEMSG msgs;
    FlushMouseMsgBuffer();
    ExMessage exm;
    bool lbtdown = false;
    TCHAR LastChar = TCHAR('\0');
    seg::String dbg2;
    while (true)
    {
        seg::render::fill(this->bg);
        for (int i = 0; i < this->num; i++)
        {
            this->cbs[i]._render();
        }
        if (this->CreateAutoQuitButton == true)
        {
            setlinecolor(RGB(20, 200, 120));
            setfillcolor(RGB(20, 80, 252));
            fillrectangle(0, 0, 50, 50);
            setlinestyle(PS_SOLID, 4);
            line(5, 5, 45, 45);
            line(5, 45, 45, 5);
        }
        getmessage(&exm, EX_MOUSE | EX_KEY | EX_CHAR | EX_WINDOW); //https://tieba.baidu.com/p/7457305944
        switch (exm.message)
        {
        case WM_LBUTTONDOWN:
            lbtdown = true;
            msgs.x = exm.x;
            msgs.y = exm.y;
            if (this->ShowDebugInfo == true)
            {this->dbgifo.set(L"LB ");
            this->dbgifo.join((int)(exm.x));
            this->dbgifo.join(wchar_t(','));
            this->dbgifo.join((int)(exm.y));
            this->dbgifo_livetime = 125 * 2.080;}
            break;
        case WM_CHAR:
            LastChar = exm.ch;
            if (this->ShowDebugInfo == true)
            {this->dbgifo.set(L"CH ");
            this->dbgifo.join(exm.ch);
            this->dbgifo_livetime = 125 * 2.080;}
            break;
        default:
            break;
        }
        if (lbtdown == true)
        {
            if (this->CheckChoose(msgs))
            {
                seg::render::flip();
                //setlinestyle(&OldLS);
                return;
            }
        }
        if (this->ChooseID >= 0 && this->cbs[this->ChooseID].ClickAsInput == true)
        {
            if (LastChar != ((TCHAR)('\0')))
            {
                if (LastChar != ((TCHAR)('\b')))
                this->cbs[this->ChooseID].s.join((wchar_t)LastChar);
                else if (LastChar == ((TCHAR)('\b')))
                    this->cbs[this->ChooseID].s.back();
            }
        }
        lbtdown = false;
        LastChar = ((TCHAR)('\0'));
        if (this->ShowDebugInfo == true)
        {
            if (this->dbgifo_livetime > 0)
            {
                this->dbgifo_livetime--;
                seg::render::text(getwidth()-1, getheight()-1, (TCHAR*)(this->dbgifo.get()), seg::render::Place::rightbottom);
                dbg2.set(L"");
                dbg2.join(this->dbgifo_livetime);
                seg::render::text(0, getheight()-1, (TCHAR*)(dbg2.get()), seg::render::Place::leftbottom);
            }
        }
        seg::render::flip();
        Sleep(8);
    }
}

// include A-Z, 0-9, num 0-9, F1-F12, .(0x6E)
int seg::Key::GetRecentRealKey()
{
    if (GetForegroundWindow() != seg::MainHandle)
    {
        return 0;
    }
    for (int i = 0x30; i <= 0x39; i++)
    {
        if (seg::Key::GetGlobalKeyPressed(i))
        {
            return i;
        }
    }
    for (int i = 0x60; i <= 0x69; i++)
    {
        if (seg::Key::GetGlobalKeyPressed(i))
        {
            return i - 0x60;
        }
    }
    if (seg::Key::GetGlobalKeyPressed(VK_SPACE)) return int(L' ');
    if (seg::Key::GetGlobalKeyPressed(VK_BACK)) return 0x08;
    if (seg::Key::GetGlobalKeyPressed(VK_RETURN)) return 0x0D;
    for (int i = 0x41; i < 0x5B; i++)
    {
        if (seg::Key::GetGlobalKeyPressed(i))
        {
            if (seg::Key::GetGlobalKeyPressed(VK_SHIFT))
            {
                return i;
            }
            else {
                return i + 'a' - 'A';
            }
        }
    }
    return 0;
}

inline bool seg::Key::GetGlobalKeyPressed(int VK)
{
    return GetAsyncKeyState(VK)& 0x8000 ? 1:0;
}

int seg::Key::GetVirtualKey(int VK)
{
    if (GetForegroundWindow() != seg::MainHandle)
    {
        return 0;
    }
    return GetAsyncKeyState(VK)& 0x8000 ? 1:0;
}


seg::String::String(){
    ;
}

int seg::String::len()
{
    return this->_len;
}

int seg::String::join(const wchar_t * const buf)
{
    const wchar_t * newbuf = buf;
    int o = 0;
    while ((*newbuf != (wchar_t)('\0'))  &&  (this->_len < Seg_StringLengthMax))
    {
        this->s[this->_len] = *newbuf;
        newbuf++;
        this->_len += 1;
        o++;
    }
    return o;
}

bool seg::String::join(const wchar_t c)
{
    if (this->_len < Seg_StringLengthMax)
    {
        if (iswprint(c))
        {
            this->s[this->_len] = c;
            this->_len += 1;
            return true;
        }
        else
        {
            return false;
        }
    }
    else
    {
        return false;
    }
}

void seg::String::join(int d)
{
    wchar_t s[18];
    swprintf(s, L"%d", d);
    this->join(s);
}

void seg::String::join(double f)
{
    wchar_t s[26];
    swprintf(s, L"%lf", f);
    this->join(s);
}

void seg::String::join(bool b)
{
    if (b)
    {
        this->join(L"True");
    }
    else
    {
        this->join(L"False");
    }
}

void seg::String::back()
{
    this->s[this->_len] = (wchar_t)'\0';
    if (this->_len > 0)
    {
        this->_len -= 1;
        this->s[this->_len] = (wchar_t)'\0';
    }
}

void seg::String::set(const wchar_t *const buf)
{
    for (int i = 0; i < Seg_StringLengthMax; i++)
    {
        this->s[i] = ((wchar_t)('\0'));
    }
    this->_len = 0;
    this->join(buf);
}

const wchar_t * seg::String::get()
{
    return this->s;
}

// Pillow lib
#ifndef GetArrayIndex
#define GetArrayIndex(x, y, X, Y) ((y * X + x))
#define GetIndex2Y(x, y, X, Y) ((GetArrayIndex(x, (Y-y - 1), X, Y)))
#endif
void seg::Pillow::Flip(int direct, IMAGE * src, IMAGE * out)
{
    const int w = src->getwidth();		// 获取源图像宽度
	const int h = src->getheight();		// 获取源图像高度

	// 如果目标图像与源图像尺寸不同，调整目标图像
	if (out->getwidth() != w || out->getheight() != h)
		out->Resize(w, h);

	// 获取源图像与目标图像的显示缓冲区地址
	DWORD* s = GetImageBuffer(src);
	DWORD* d = GetImageBuffer(out);

    if (direct & seg::Pillow::Directs::X)
	{
        // 左右翻转每一行的像素
        for (int x = 0; x < w; x++)
            for (int y = 0; y < h; y++)
                d[y * w + w - x - 1] = s[y * w + x]; // ??? -1 or not?
    }
    if (direct & seg::Pillow::Directs::Y)
    {
        // 上下翻转
        for (int x = 0; x < w; x++)
        {
            for (int y = 0; y < h; y++)
            {
                d[GetIndex2Y(x, y, w, h)] = s[GetArrayIndex(x, y, w, h)];
            }
        }
    }
    return;
}

void seg::Pillow::transparentimage(IMAGE *dstimg, int x, int y, IMAGE *srcimg, UINT transparentcolor)
{
    // 变量初始化
    DWORD *dst = GetImageBuffer(dstimg);
    DWORD *src = GetImageBuffer(srcimg);
    int src_width = srcimg->getwidth();
    int src_height = srcimg->getheight();
    int dst_width = (dstimg == NULL ? getwidth() : dstimg->getwidth());
    int dst_height = (dstimg == NULL ? getheight() : dstimg->getheight());

    // 计算贴图的实际长宽
    int iwidth = (x + src_width > dst_width) ? dst_width - x : src_width;
    int iheight = (y + src_height > dst_height) ? dst_height - y : src_height;

    // 修正贴图起始位置
    dst += dst_width * y + x;

    // 修正透明色，显示缓冲区中的数据结构为 0xaarrggbb
    transparentcolor = 0xff000000 | BGR(transparentcolor);

    // 实现透明贴图
    for (int iy = 0; iy < iheight; iy++)
    {
        for (int ix = 0; ix < iwidth; ix++)
        {
            if (src[ix] != transparentcolor)
                dst[ix] = src[ix];
        }
        dst += dst_width;
        src += src_width;
    }
}

seg::Pillow::PIC::PIC(IMAGE * self , int ColorKey )
{
    if (self != NULL)
    {
        this->img = *(self);
    }
    this->colorkey = (color_t)ColorKey;
}

inline void seg::Pillow::PIC::ToBuffer()
{
    this->IMGBUF = GetImageBuffer(&(this->img));
}


bool seg::Pillow::PIC::load(wchar_t *filename, bool _raise )
{
    // file exists?
    if (PathFileExistsW((LPCWSTR)filename))
    {
        loadimage(&this->img, (LPCTSTR)filename, 0, 0, true);
        return true;
    }
    else{
        if (_raise == true)
        {
            wchar_t filename1[300];
            wsprintfW(filename1, L"File %s doesn't exists.", filename);
            seg::Traceback::raise(L"FileNotExistError", L"seg.pillow.pic.load", filename1);
        }
        return false;
    }
}

void seg::Pillow::PIC::blit(int x, int y)
{
    if (this->colorkey < 0 || this->colorkey > 255)
    {
        putimage(x, y, &(this->img));
    }
    else
    {
        COLORREF color = RGB(this->AlphaColor.r, this->AlphaColor.g, this->AlphaColor.b);
        DWORD *imgp = GetImageBuffer(&(this->img));
        DWORD *bgimgp = GetImageBuffer();
        int w, bw, h, i, j;
        w = this->img.getwidth();
        bw = getwidth();
        const int MaxXY = bw * getheight();
        h = this->img.getheight();
        color += 0xff000000;
        for (i = 0; i < h; ++i)
        {
            for (j = 0; j < w; ++j)
            {
                if (imgp[i*w + j] != color && ((i + y)*bw + j + x) < MaxXY)
                    bgimgp[(i + y)*bw + j + x] = imgp[i*w + j];
            }
        }
        //FlushBatchDraw();
    }
}

void seg::Pillow::PIC::blit(int x, int y, int alpha)
{
    DWORD *imgp = GetImageBuffer(&(this->img));
	DWORD *bgimgp = GetImageBuffer();
    COLORREF color = RGB(this->AlphaColor.r, this->AlphaColor.g, this->AlphaColor.b);
    const int &dstx = x;
    const int & dsty = y;
    //const int xymax = seg::Window.GetWidth() * seg::Window.GetHeight();
	int w, bw, h, i, j;
	w = this->img.getwidth();
	bw = getwidth();
    const int MaxXY = bw * getheight();
	h = this->img.getheight();
	color += 0xff000000;
	if (alpha < 0)alpha = 0;
	else if(alpha>255)alpha=255;
	for (i = 0; i < h; ++i)
		for (j = 0; j < w; ++j)
			if (imgp[i*w + j] != color  && ((i + y)*bw + j + x) < MaxXY)
				bgimgp[(i + y)*bw + j + x] =
                RGB
                (
				    ((int)((double)alpha / 255.0* GetRValue(imgp[i*w + j]) + (1 - alpha / 255.0)* GetRValue(bgimgp[(i + dsty)*bw + j + dstx]))),
					((int)((double)alpha / 255.0* GetGValue(imgp[i*w + j]) + (1 - alpha / 255.0)* GetGValue(bgimgp[(i + dsty)*bw + j + dstx]))),
					((int)((double)alpha / 255.0* GetBValue(imgp[i*w + j]) + (1 - alpha / 255.0)* GetBValue(bgimgp[(i + dsty)*bw + j + dstx])))
				);
    //FlushBatchDraw();
}

// https://pan.baidu.com/s/1sjoIWtZ
// https://tieba.baidu.com/p/3587996765
/**/
void seg::Pillow::PIC::scale(IMAGE* P,IMAGE* Q,double ZoomRate,bool HighQuality,double ZoomRate2)
{
    //����д�ڶ����Ų�����Ĭ�Ϻ͵�һ���
    if(ZoomRate2==0)
        ZoomRate2=ZoomRate;

    //�������ű����趨Ŀ��ͼ���С
    const int QW = Q->getwidth();
    const int QH = Q->getheight();
    P->Resize((int)(QW*ZoomRate),(int)(QH*ZoomRate2));
    const int PW = P->getwidth();
    const int PH = P->getheight();

    //�ֱ��ԭͼ���Ŀ��ͼ���ȡָ��
    DWORD* M=GetImageBuffer(P);
    DWORD* N=GetImageBuffer(Q);

    //ѡ���������ʹ��˫���Բ�ֵ�㷨
    if(HighQuality)
    {
        for(int i=0;i<PH - 1;i++)
        {
            for(int j=0;j<PW - 1;j++)
            {
                //���Ŀ��ͼ���Ӧ���ص���ԭͼ�ĸ������겢ȡ��
                int X_=(int)((j+0.5)/ZoomRate-0.5);
                int Y_=(int)((i+0.5)/ZoomRate2-0.5);

                //����ȡ��������A1(X,Y), A2(X+1,Y), A3(X,Y+1), A4(X+1,Y+1)�����������ٽ�4�������ɫƽ��ֵ��
                M[j+i*PW] =
                RGB
                (
                    (GetRValue(N[X_+Y_*QW])+GetRValue(N[(X_+1)+Y_*QW])+GetRValue(N[X_+(Y_+1)*QW])+GetRValue(N[(X_+1)+(Y_+1)*QW]))/4,
                    (GetGValue(N[X_+Y_*QW])+GetGValue(N[(X_+1)+Y_*QW])+GetGValue(N[X_+(Y_+1)*QW])+GetGValue(N[(X_+1)+(Y_+1)*QW]))/4,
                    (GetBValue(N[X_+Y_*QW])+GetBValue(N[(X_+1)+Y_*QW])+GetBValue(N[X_+(Y_+1)*QW])+GetBValue(N[(X_+1)+(Y_+1)*QW]))/4
                );
            }
        }
    }
    else
    //ѡ��������򰴳��淽������
    {
        for(int i=0;i<PH - 1 ;i++)
            for(int j=0;j<PW -1;j++)
                //����Ŀ��ͼ�����ص�λ��������ԭͼ�����ص㸳ֵ
                M[j+i*PW]=N[(int)(j/ZoomRate)+(int)(i/ZoomRate2)*QW];
    }
}

void seg::Pillow::PIC::scale(double ZoomRate,bool HighQuality,double ZoomRate2)
{
    if(ZoomRate2==0)
        ZoomRate2=ZoomRate;
    IMAGE tmp;
    IMAGE *P = &tmp;
    IMAGE *Q = &(this->img);

    const int QW = Q->getwidth();
    const int QH = Q->getheight();
    P->Resize((int)(QW*ZoomRate),(int)(QH*ZoomRate2));
    const int PW = P->getwidth();
    const int PH = P->getheight();

    DWORD* M=GetImageBuffer(P);
    DWORD* N=GetImageBuffer(Q);

    if(HighQuality)
    {
        for(int i = 0 ; i < PH - 1 ; i ++)
        {
            for(int j = 0 ; j <  PW  - 1 ; j++)
            {
                int X_=(int)((j+0.5)/ZoomRate-0.5);
                int Y_=(int)((i+0.5)/ZoomRate2-0.5);
                M[j+i*PW] =
                RGB
                (
                    (GetRValue(N[X_+Y_*QW])+GetRValue(N[(X_+1)+Y_*QW])+GetRValue(N[X_+(Y_+1)*QW])+GetRValue(N[(X_+1)+(Y_+1)*QW]))/4,
                    (GetGValue(N[X_+Y_*QW])+GetGValue(N[(X_+1)+Y_*QW])+GetGValue(N[X_+(Y_+1)*QW])+GetGValue(N[(X_+1)+(Y_+1)*QW]))/4,
                    (GetBValue(N[X_+Y_*QW])+GetBValue(N[(X_+1)+Y_*QW])+GetBValue(N[X_+(Y_+1)*QW])+GetBValue(N[(X_+1)+(Y_+1)*QW]))/4
                );
            }
        }
    }
    else
    {
        for(int i=0;i<PH-1;i++)
            for(int j=0;j<PW-1;j++)
                M[j+i*PW]=N[(int)(j/ZoomRate)+(int)(i/ZoomRate2)*QW];
    }
    this->img = tmp;
}


inline void seg::Pillow::PIC::rotate(IMAGE *dstimg,IMAGE *srcimg,double radian,COLORREF bkcolor ,bool autosize ,bool highquality )
{
    rotateimage(dstimg, srcimg, radian * (3.141592653589 / 180), bkcolor, autosize, highquality);
}


inline void seg::Pillow::PIC::rotate(double radius,COLORREF bkcolor ,bool autosize ,bool highquality )
{
    IMAGE tmp;
    rotateimage(&tmp, &(this->img), radius * (3.141592653589 / 180), bkcolor, autosize, highquality);
    this->img = tmp;
}


void seg::Traceback::raise(wchar_t *en, wchar_t *fn, wchar_t *ed)
{
    throw seg::Traceback::Exception(en, fn, ed);
}

seg::Traceback::Exception::Exception(wchar_t * ErrName, wchar_t *FuncName, wchar_t* ErrDesc)
{
    this->errName.set(ErrName);
    this->funcName.set(FuncName);
    this->errDesc.set(ErrDesc);
    this->format_exc.set(L"Program crashed! An exception occured: ");
    this->format_exc.join(ErrName);
    this->format_exc.join(L"\n  at ");
    this->format_exc.join(FuncName);
    this->format_exc.join(L"\nDetails: ");
    this->format_exc.join(ErrDesc);
}



int seg::String::count_line(wchar_t sep )
{
    int ret = 0;
    for (size_t i = 0; i < this->_len; i++)
    {
        if (this->s[i] == sep)
        {
            ret++;
        }
    }
    ret ++;
    return ret;
}
bool seg::String::get_line(wchar_t * out, int line , wchar_t sep )
{
    bool ret = false;
    int linenow = 0;
    for (size_t i = 0; i < this->_len; i++)
    {
        if (linenow == line)
        {
            // readstr
            int k = 0;
            for (size_t j = i; j < this->_len; j++)
            {
                if (this->s[j] == sep)
                {
                    break;
                }
                else
                {
                    out[k] = this->s[j];
                    k++;
                }
            }

            out[k] = (wchar_t)('\0');
            return true;
        }
        if (this->s[i] == sep)
        {
            linenow++;
        }
    }
    return false;
}

bool seg::String::isint()
{
    for (size_t i = 0; i < this->_len; i++)
    {
        if (this->s[i] >= (wchar_t)('0') && this->s[i] <= (wchar_t)('9'))
        {
            pass;//ok
        }
        else {
            return false;
        }
    }
    return true;
}

bool seg::String::isfloat()
{
    int i = 0;
    for ( i = 0; i < this->_len; i++)
    {
        if (this->s[i] >= (wchar_t)('0') && this->s[i] <= (wchar_t)('9'))
        {
            pass;//ok
        }
        else if (this->s[i] == (wchar_t)('.'))
        {
            break;
        }
        else {
            return false;
        }
    }
    i++;
    if (i >= this->_len)
    {
        return false;
    }
    for (; i < this->_len; i++)
    {
        if (this->s[i] >= (wchar_t)('0') && this->s[i] <= (wchar_t)('9'))
        {
            pass;//ok
        } else {
            return false;
        }
    }
    return true;
}

bool seg::String::isbool()
{
    if (this->equals(L"True"))
    {
        return true;
    }
    else if (this->equals(L"False"))
    {
        return true;
    }
    return false;
}

bool seg::String::IsCppBool()
{
    if (this->equals(L"true"))
    {
        return true;
    }
    else if (this->equals(L"false"))
    {
        return true;
    }
    return false;
}

bool seg::String::isint(int start)
{
    if (start >= this->_len)
    {
        return false;
    }
    for (size_t i = start; i < this->_len; i++)
    {
        if (this->s[i] >= (wchar_t)('0') && this->s[i] <= (wchar_t)('9'))
        {
            pass;//ok
        }
        else {
            return false;
        }
    }
    return true;
}

bool seg::String::isfloat(int start)
{
    int i = start;
    for ( i = start; i < this->_len; i++)
    {
        if (this->s[i] >= (wchar_t)('0') && this->s[i] <= (wchar_t)('9'))
        {
            pass;//ok
        }
        else if (this->s[i] == (wchar_t)('.'))
        {
            break;
        }
        else {
            return false;
        }
    }
    i++;
    if (i >= this->_len)
    {
        return false;
    }
    for (; i < this->_len; i++)
    {
        if (this->s[i] >= (wchar_t)('0') && this->s[i] <= (wchar_t)('9'))
        {
            pass;//ok
        } else {
            return false;
        }
    }
    return true;
}

int seg::String::cint()
{
    int ret = 0;
    if (this->isint() ||  ( this->s[0] == wchar_t('-')  &&  this->isint(1))  )
    {
        swscanf(this->s, L"%d", &ret);
        return ret;
    }
    else
        pass;
    return -114514;
}

double seg::String::cfloat()
{
    double ret = 0.0;
    if (this->isfloat() ||  ( this->s[0] == wchar_t('-')  &&  this->isfloat(1))  )
    {
        swscanf(this->s, L"%lf", &ret);
        return ret;
    }
    else
        pass;
    return -114514.1919810;
}

bool seg::String::cbool()
{
    if (this->equals(L"True") || this->equals(L"true"))
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool seg::String::cbool(bool _raise)
{
    if (this->equals(L"True") || this->equals(L"true"))
    {
        return true;
    }
    else if (this->equals(L"False") || this->equals(L"false"))
    {
        return false;
    }
    else
    {
        seg::Traceback::raise(L"ValueError", L"seg.string.cbool", L"string不是有效的布尔值");
    }
    return false;
}

int seg::String::find(wchar_t c, int index)
{
    int ind = index;
    for ( ;   ind < this->_len  ;    ind++   )  {
        if (this->s[ind] == c)
        {
            return ind;
        }
        else{
            pass;
        }
    }
    return -114514;
}

seg::String seg::String::lstrip(wchar_t *c)
{
    seg::String s;
    seg::String From;
    s.set(L"");
    From.set(c);
    const wchar_t *ptr = this->get();
    int i = 0;
    while (From.find(*ptr, 0) >= 0)
    {
        ptr++;
        i++;
    }
    if (i >= this->_len)
    {
        return s;
    }
    else
    {
        s.set(ptr);
        return s;
    }
}

// new
seg::String seg::String::rstrip(wchar_t * c)
{
    seg::String str;
    str.set(this->get());
    str.rstrip(c, false);
    return str;
}

// inplace
void seg::String:: rstrip(wchar_t * c, bool get_a_new_string )
{
    if (this->_len < 1)
    {
        return;
    }
    else
    {
        seg::String From;
        From.set(c);
        while (this->_len > 0)
        {
            if (From.find(this->s[this->_len - 1], 0) >= 0)
            {
                this->back();
            }
            else
            {
                break;
            }
        }
        return;
    }
}

seg::String seg::String::strip(wchar_t *c)
{
    seg::String str = this->lstrip(c);
    str.rstrip(c, false);
    return str;
}

// 2022 create. 20230926 last change.
// 祖传代码请勿乱动 ^v^
bool seg::String::startswith(wchar_t *s)
{
	const wchar_t *ss = this->get();
    const wchar_t *buf = s;
CmPnExT:
	if (*buf == wchar_t('\0')){
		return true;
	} if (*ss == wchar_t('\0')){
		return false;
	}
	if (*buf != *ss) {
		return false;
	}
	ss ++;
	buf++;
	goto CmPnExT;
	return 0;
}

bool seg::String::endswith(wchar_t *s)
{
    int l = 0;
    while (s[l] != (wchar_t)'\0')
    {
        l++;
    }
    if (l > this->len())
    {
        return false;
    }
    l--;
    int i = this->_len - 1;
    while (l >= 0)
    {
        if (i < 0)
        {
            return false;
        }
        if (s[l] != this->s[i])
        {
            return false;
        }
        l--;
        i--;
    }
    return true;
}

int seg::String::FindFrom(const wchar_t * const slist)
{
    int ret = 0;
    const wchar_t * sl = slist;
    for (int j = 0; j < this->_len; j++)
    {
        sl = slist;
        while (*sl != wchar_t('\0'))
        {
            if (this->s[j] == *sl)
            {
                ret++;
                break; // 同一字符只计算一次
            }
            sl ++;
        }
    }
    return ret;
}


seg::String seg::String::reverse()
{
    seg::String str;
    str.set(L"\0");
    int i = this->_len - 1;
    while (i >= 0)
    {
        str.join(this->s[i]);
        i--;
    }
    return str;
}


bool seg::String::equals( const wchar_t *s)
{
    const wchar_t *ss = this->get();
    const wchar_t *buf = s;
CmPnExT2:
	if (*buf == wchar_t('\0')){
        if (*ss == *buf)
    		return true;
        else
            return false;
	} if (*ss == wchar_t('\0')){
		if (*ss == *buf)
    		return true;
        else
            return false;
	}
	if (*buf != *ss) {
		return false;
	}
	ss ++;
	buf++;
	goto CmPnExT2;
	return 0;
}


bool seg::String:: operator ==(const wchar_t *s)
{
    int i = 0;
    while (i < this->_len)
    {
        if (this->s[i] != s[i])
        {
            return false;
        }
        if (s[i] == wchar_t('\0'))
        {
            break;
        }
        i++;
    }
    if (this->s[i] == s[i])
        return true;
    else
        return false;
}


bool seg::String::operator== (seg::String &ss)
{
    if (ss.len() != this->_len)
    {
        return false;
    }
    return this->startswith(const_cast<wchar_t *>(ss.get()));
}


seg::String seg::String::get(int start, int stop , int step )
{
    seg::String str;
    str.set(L"");
    if (stop < 0)
    {
        stop = stop + this->_len + 1;
    }
    if (start >= stop || start < 0 || stop >= this->_len)
    {
        // ERROR
        return str;
    }
    for ( ; start < stop; start += step)
    {
        str.join(this->s[start]);
    }
    return str;
}

wchar_t seg::String::index(int _index)
{
    if (_index < 0)
    {
        _index = _index + this->_len;
    }
    if (_index >= 0 && _index < this->_len)
        return this->s[_index];
    else
        return (wchar_t)'\0';
}

wchar_t seg::String::operator[](int _index)
{
    if (_index < 0)
    {
        _index = _index + this->_len;
    }
    if (_index >= 0 && _index < this->_len)
        return this->s[_index];
    else
        return (wchar_t)'\0';
}

wchar_t seg::String::operator[](int _index) const
{
    if (_index < 0)
    {
        _index = _index + this->_len;
    }
    if (_index >= 0 && _index < this->_len)
        return this->s[_index];
    else
        return (wchar_t)'\0';
}

/*x, y: pos
yadd: 行距*/
void seg::render::multiline(int x, int y, seg::String str, int yadd, seg::render::Place CharacterPos)
{
    const int lineA = str.count_line();
    wchar_t buf[Seg_StringLengthMax];
    for (int i = 0; i <= lineA; i++)
    {
        if (!str.get_line(buf, i))
        {
            break;
        }
        else
        {
            seg::render::text(x, y + i * yadd, (TCHAR*)buf, CharacterPos);
        }
    }

}

int seg::Traceback::errorscreen(seg::Traceback::Exception & se)
{
    seg::Interface errscr(2, RGB(236, 218, 192), true);
    errscr.cbs[0].init(
        120, 120, getwidth()*0.6, getheight()*0.48, 3, 0, RGB(255, 60, 236), RGB(10, 30, 69), true, 0, false, false
    );
    errscr.cbs[1].init
    (
        getwidth() * 0.6, getwidth() * 0.69, getwidth()*0.6, getheight()*0.23, 3, 0,
        RGB(255, 60, 236), RGB(10, 30, 69), true, 0, false, false
    );
    errscr.cbs[0].s.set(se.format_exc.get());
    errscr.cbs[1].s.set(L"From: UserDefine");
    errscr.mainloop();
    return 0;
}
int seg::Traceback::errorscreen(const std::exception &e)
{
    seg::Interface errscr(2, RGB(20, 30, 60), true);
    errscr.cbs[0].init(
        120, 120, getwidth()*0.6, getheight()*0.48, 3, 0, RGB(255, 60, 236), RGB(10, 30, 69), true, 0, false, false
    );
    errscr.cbs[1].init
    (
        getwidth() * 0.6, getwidth() * 0.69, getwidth()*0.6, getheight()*0.23, 3, 0,
        RGB(255, 60, 236), RGB(10, 30, 69), true, 0, false, false
    );
    errscr.cbs[0].s.set(L"Unknown exception");
    errscr.cbs[1].s.set(L"From: System");
    errscr.mainloop();
    return 0;
}


int seg::Math::randint(int _min, int _max)
{
    std::uniform_int_distribution<int> ri(_min, _max);
    return ri(seg::Math::eng);
}

double seg::Math::random(double _min, double _max)
{
    std::uniform_real_distribution<double> ru(_min, _max);
    return ru(seg::Math::eng);
}


seg::Interface::~Interface()
{
    delete[] this->cbs;
}


/*
#################

基本类型(int + double + wchar_t* + dict)
可用于读取配置文件, 不建议做复杂工作

*****************
*/

int seg::String::count(wchar_t c)
{
    int ret = 0;
    for (int j = 0; j < this->_len; j++)
    {
        if (this->s[j] == c)
        {
            ret++;
        }
    }
    return ret;
}

void seg::String::from_cchar(const char *cs)
{
    this->set(L"");
    wchar_t str[Seg_StringLengthMax];
    swprintf(str, L"%hs", cs);
    this->join((const wchar_t *)str);
}

void seg::String::to_cchar(char *out)
{
    sprintf(out, "%ws", this->get());
}

seg::SegObject_BasicObject::SegObject_BasicObject()
{
    this->_t = seg::SegType::sNone;
}

seg::SegType seg::SegObject_BasicObject::ctype()
{
    return this->_t;
}

int seg::SegObject_BasicObject::intvar()
{
    return this->var.d;
}

double seg::SegObject_BasicObject::floatvar()
{
    return this->var.f;
}

seg::String & seg::SegObject_BasicObject::strvar()
{
    return this->s;
}

void seg::SegObject_BasicObject::set(int v)
{
    this->_t = seg::SegType::sint;
    this->var.d = v;
}

void seg::SegObject_BasicObject::set(double v)
{
    this->_t = seg::SegType::sfloat;
    this->var.f = v;
}

void seg::SegObject_BasicObject::set(const wchar_t *s)
{
    this->_t = seg::SegType::sstr;
    this->s.set(s);
}

void seg::SegObject_BasicObject::set(seg::String &s)
{
    this->_t = seg::SegType::sstr;
    this->s.set(s.get());
}

void seg::SegObject_BasicObject::set(bool bo)
{
    this->_t = seg::SegType::sbool;
    this->var.b = bo;
}

void seg::SegObject_BasicObject::set(seg::SegObject_BasicObject & b)
{
    switch (b._t)
    {
        case seg::SegType::sstr:
        {
            this->s.set(b.s.get());
            break;
        }
        case seg::SegType::sNone:
        {
            break;
        }
        case seg::SegType::sint:
        {
            this->var.d = b.var.d;
            break;
        }
        case seg::SegType::sfloat:
        {
            this->var.f = b.var.f;
            break;
        }
        case seg::SegType::sbool:
        {
            this->var.b = b.var.b;
            break;
        }
        default:
        {
            return;
        }
    }
    this->_t = b._t;
}

seg::SegObject_DictObject::SegObject_DictObject()
{
    this->_len = 0;
    for (int i = 0; i < Seg_ListLengthMax; i++)
    {
        this->isused[i] = false;
    }
}

int seg::SegObject_DictObject::len()
{
    return this->_len;
}

bool seg::SegObject_DictObject::set(seg::String &__key, seg::SegObject_BasicObject &__value)
{
    if (this->_len >= Seg_ListLengthMax)
    {
        return false;
    }
    else
    {
        for (int i = 0; i < Seg_ListLengthMax; i++)
        {
            if (this->isused[i] == false)
            {
                this->isused[i] = true;
                this->_key[i].set(__key.get());
                this->_value[i].set(__value);
                this->_len ++;
                return true;
            }
        }
    }
    return false;
}

bool seg::SegObject_DictObject::set(const wchar_t *__key_, seg::SegType t, int d, double f , bool b, const wchar_t * s )
{
    if (this->_len >= Seg_ListLengthMax)
    {
        return false;
    }
    else
    {
        for (int i = 0; i < Seg_ListLengthMax; i++)
        {
            if (this->isused[i] == false)
            {
                this->isused[i] = true;
                this->_key[i].set(__key_);
                switch (t)
                {
                    case seg::SegType::sbool:
                    {
                        this->_value[i].set(b);
                        break;
                    }
                    case seg::SegType::sint:
                    {
                        this->_value[i].set(d);
                        break;
                    }
                    case seg::SegType::sfloat:
                    {
                        this->_value[i].set(f);
                        break;
                    }
                    case seg::SegType::sstr:
                    {
                        this->_value[i].set(s);
                        break;
                    }
                    default:
                    {
                        seg::Traceback::raise(L"TypeError", L"seg.dict.set@pdfbp", L"类型错误: 无法设置该类型");
                        break;
                    }
                }
                this->_len ++;
                return true;
            }
        }
    }
    return false;
}

int seg::SegObject_DictObject::FromStr(seg::String & str)
{
    int ret = 0;
    int num_m = 0; // :
    int num_d = 0; // ,
    num_m = str.count((wchar_t)':');
    num_d = str.count((wchar_t)',');
    if (num_m != num_d + 1)
    {
        return 0;
    }
    seg::String buf;
    buf.set(L"");
    seg::String buf2;
    buf2.set(L"");
    wchar_t s[Seg_StringLengthMax];
    wchar_t *sp = NULL;
    bool bb = false;
    seg::String tk; // temp key
    seg::String tv;
    seg::SegObject_BasicObject bsc;
    //tk.set(L"");
    //tv.set(L"");
    for (int i = 0; i < num_m; i++)
    {
        bb = str.get_line(s, i, wchar_t(','));
        if (bb == false)
        {
            return ret;
        }
        buf.set(s);
        buf2 = buf.strip(L" :");
        if (buf2.count(wchar_t(':')) != 1)
        {
            return ret;
        }
        sp = const_cast<wchar_t *>(buf2.get());
        //buf.set(L"");
        tk.set(L"");
        while (iswalnum((wint_t)(*sp)) /*== true*/)
        {
            tk.join(*sp);
            sp++;
        }
        while (*sp != wchar_t(':'))
        {
            sp++;
        }
        sp++;
        while ((*sp != wchar_t('\0'))  &&  (iswalnum((wint_t)(*sp))  ==  false))
        {
            sp ++;
        }
        if (*sp == wchar_t('\0'))
        {
            return ret;
        }
        tv.set(L"");
        while (*sp != wchar_t('\0'))
        {
            tv.join(*sp);
            sp++;
        }
        if (tv.isint() || (tv[0] == wchar_t('-')  &&  tv.isint(1)))
        {
            bsc.set((int)(tv.cint()));
        } else if (tv.isfloat()  ||  (tv[0] == wchar_t('-')  &&  tv.isfloat(1)))
        {
            bsc.set((double)(tv.cfloat()));
        } else
        {
            bsc.set(tv.get());
        }
        if (this->set(tk, bsc))
        {
            ret   ++     ;
        }
    }
    return ret;
}

using String = seg::String;

#endif // _SEG_LIBC
